package org.javahispano.javacup.tacticas_aceptadas.fonsy.asnft.tactica;

//import asnft.entrenamiento.*;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;
import java.util.Arrays;
import org.javahispano.javacup.modelo.*;

public class ASNFT implements Tactica {
    //Clase para controlar los marcajes a los contrarios.
    private final class Marcajes{
        private int[] Marca ={
            -1,-1,-1,-1,-1,-1,
            -1,-1,-1,-1,-1
        };
        private double[] cacheVelJugadores ={
            -1,-1,-1,-1,-1,-1,
            -1,-1,-1,-1,-1
        };
        public void addMarcaje(int idJugador, int idRival){
            Marca[idJugador]=idRival;
        }
        public void delMarcaje(int idJugador){
            Marca[idJugador]=-1;
        }
        public boolean estaMarcando (int idJugador){
            return (Marca[idJugador]!=-1);
        }
        public boolean estaEnPosicion (int idJugador){
            boolean Resp = false;
            if (Marca[idJugador]!=-1){
                Posicion Rival = Rivales[Marca[idJugador]];
                Posicion Jugador = Propios[idJugador];
                Resp = (Rival.distancia(Jugador)>distMarcaje(idJugador));
            }
            return Resp;
        }
        public int estaMarcado(int idRival){
            return Arrays.binarySearch(Marca,idRival);
        }
        public Posicion getMarcaje(int idJugador){
            if (Marca[idJugador] != -1){
                Posicion Rival = Rivales[Marca[idJugador]];
                return Rival.moverAngulo(Rival.angulo(posBalon), distMarcaje(idJugador));
            }else{
                return null;
            }
        }
        private double distMarcaje(int idJugador){
            if (cacheVelJugadores[idJugador]==-1){
                cacheVelJugadores[idJugador]=Constantes.getVelocidad(Detalle.getJugadores()[idJugador].getVelocidad());
            }
            //Constantes.getVelocidad(1)*2 = 1.2 (asumiendo la máxima velocidad del rival)
            return 1.2/cacheVelJugadores[idJugador];
        }
    }
    
    //Clase para evaluar el éxito de las voleas contra el rival actual.
    private class chutesVolea{
        private boolean Chutado = false;
        //TODO: Poner la media de los entrenamientos.
        //private int Intentos = Exitos.inVoleas();
        //private int Aciertos = Exitos.acVoleas();
        private int Intentos = 2;
        private int Aciertos = 2;

        public boolean enCamino(){
            return Chutado;
        }
        public double Efectividad(){
            double Resp = 1;
            if (Intentos!=0){
                Resp = (double)(Aciertos/Intentos);
            }
            return Resp;
        }
        public void setChute(){
            Chutado = true;
            Intentos++;
        }
        public void setExito(boolean Gol){
            Chutado = false;
            if (Gol){
                Aciertos++;
            }
        }
    }
    private class Maniobras{
        private final class Paso{
            public int idJugador = -1;
            public Posicion irA = null;
            public int pasarA = -1;
            public boolean Completo = false;
        }
        private int nroPasos = 0;
        private boolean enMarcha = false;
        private Paso[] Lista ={
              new Paso(), new Paso(), new Paso(), new Paso(), new Paso(),
              new Paso(), new Paso(), new Paso(), new Paso(), new Paso()
        };

        public boolean hayManiobra(){
            return (nroPasos>0);
        }
        public boolean Ejecutando(){
            return enMarcha;
        }
        public boolean primerPaso(){
            //Devuelve verdadero si el primer paso está completado.
            return (nroPasos == 0)?false:Lista[0].Completo;
        }
        public boolean enPosicion(){
            boolean Resp = true;
            if (!enMarcha) return !Resp;

            for (int Bucle=0; Bucle<nroPasos; Bucle++){
                //Si idJugador = -1, se acabó la lista.
                if (Lista[Bucle].idJugador == -1) break;

                if (Propios[Lista[Bucle].idJugador].distancia(Lista[Bucle].irA)>0.1){
                    Resp = false;
                    break;
                }
            }

            return Resp;
        }
        public LinkedList<Comando> Ejecutar(LinkedList<Comando> comandos,int[] puedenRematar, boolean Chutar){

            double Fuerza = 1;
            double Dist = 0;
            boolean Bombeo = true;
            Posicion Objetivo = null;


            for (int Bucle=0; Bucle<nroPasos; Bucle++){
                //Si idJugador = -1, se acabó la lista.
                if (Lista[Bucle].idJugador == -1) break;

                //Todavía no so ha podido chutar, moverlo para ponerlo en posicion.
                Objetivo = Lista[Bucle].irA;
                if (!Lista[Bucle].Completo){
                    //Si no está completo, y todavía no le han pasado, ir a la posición.
                    if (estadoPase.idDestinatario != Lista[Bucle].idJugador){
                        comandos.add(new ComandoIrA(Lista[Bucle].idJugador,Objetivo));
                    }

                    //Si está en posicion de chutar, le damos la orden y damos por finalizado el paso.
                    if (Chutar && (Arrays.binarySearch(puedenRematar,Lista[Bucle].idJugador)>-1)){
                        Dist = Propios[Lista[Bucle].idJugador].distancia(Propios[Lista[Bucle].pasarA]);
                        if (Dist>2){
                            Bombeo = (Dist>29)?true:false;
                            Fuerza = fuerzaEstimada(Dist,Lista[Bucle].idJugador);
                            //Registrar el pase.
                            estadoPase.addPase(Lista[Bucle].pasarA, -1, -1);
                            //Chutar.
                            comandos.add(new ComandoGolpearBalon(Lista[Bucle].idJugador, Propios[Lista[Bucle].pasarA], Fuerza, Bombeo));
                        }
                        //Dar por completo el paso.
                        Lista[Bucle].Completo = true;

                        //Si es el último paso, dar por terminada la maniobra.
                        enMarcha = !(nroPasos==Bucle);
                    }

                }

            }
            //Mientras queden ordenes que dar, la maniobra está en marcha.
            enMarcha = !comandos.isEmpty();
            return comandos;
        }
        public void addPaso(int idJugador, Posicion irA, int pasarA){
                Lista[nroPasos].idJugador = idJugador;
                Lista[nroPasos].irA = irA;
                Lista[nroPasos].pasarA = pasarA;
                Lista[nroPasos].Completo = false;
            nroPasos++;
        }
        public void Limpiar(){
            for (int Bucle=0; Bucle<nroPasos; Bucle++){
                Lista[Bucle].idJugador = -1;
                Lista[Bucle].Completo = false;
            }
            nroPasos = 0;
            enMarcha = false;
        }
        public Maniobras(){
            for (int Bucle=0; Bucle<nroPasos; Bucle++){
                Lista[Bucle].idJugador = -1;
            }
        }

    }

    private class Pase{
        public int idDestinatario = -1;
        public int idPase = -1;
        public int idRemitente = -1;
        public Posicion posPase = null;

        //Inicializar los datos con los historicos de los entrenamientos.
        //private int[][] Exitos = Resultados.pasesExitos();
        public int[][] Exitos = {
            {2,1},
            {3,2,1},
            {3,2,1},
            {2,1},
            {3,2,1},
            {3,2,1},
            {2,1},
            {2,1},
            {3,2,1},
            {3,2,1},
            {3,2,1}
        };
        public int seleccionaPase(int idJugador, int[] Pases){
        //Revisar la estrategia de pases según evolución del partido.
            int Resp = -1;
            //Ordenar según nivel de éxito.
            int[] Prioridad = ordenDinamico(Exitos[idJugador]);
            int nroPases = Prioridad.length;

            //Segundo orden según cercanía a la portería.
            double Norma;
            double Cercano = -1;
            for (int Bucle=0; Bucle<nroPases; Bucle++){
                if (paseViable(Propios[idJugador],Propios[Pases[Prioridad[Bucle]]])){
                    Norma = Propios[Pases[Prioridad[Bucle]]].norma(Constantes.centroArcoSup);
                    if ((Norma<Cercano)||(Cercano==-1)){
                        Cercano = Norma;
                        Resp = Prioridad[Bucle];
                    }
                }
            }
            return Resp;
        }

        public void addPase(int idDestinatario, int idPase, int idRemitente){
           if ((idDestinatario != -1) && (idPase>-1)){
           //El anterior fue fallido
               Exitos[idRemitente][idPase]--;
           }
           this.idDestinatario = idDestinatario;
           this.idPase = idPase;
           this.idRemitente = idRemitente;
           posPase = Propios[idDestinatario];
        }
        public void paseKO(){
           if ((idDestinatario != -1) && (idPase>-1)){
               Exitos[idRemitente][idPase]++;
           }
           idDestinatario = -1;
           idPase = -1;
           idRemitente = -1;
           posPase = null;
        }
        public void paseOK(){
           if ((idDestinatario != -1) && (idPase>-1)){
               Exitos[idRemitente][idPase]++;
           }
           idDestinatario = -1;
           idPase = -1;
           idRemitente = -1;
           posPase = null;
        }

        public boolean hayPase(){
            return (idDestinatario != -1);
        }
    }

    private boolean enArea(int[] Area, Posicion Elemento){
        boolean Resp = false;
        double CoorX, CoorY, Alto, Ancho;

        Alto = Area[2]-Area[0];
        Ancho = Area[3]-Area[1];
        CoorX = Area[0]+(Alto/2);
        CoorY = Area[1]+(Ancho/2);

        Resp = enArea(new Posicion(CoorX,CoorY),(int)Alto, (int)Ancho, Elemento);

        return Resp;
    }
    private boolean enArea(Posicion puntoMedio,int Alto, int Ancho, Posicion Elemento){
        boolean Resp = false;
        Posicion supIzq = new Posicion(puntoMedio.getX()-(Alto/2),puntoMedio.getY()-(Ancho/2));
        Posicion infDer = new Posicion(puntoMedio.getX()+(Alto/2),puntoMedio.getY()+(Ancho/2));

        if ((Elemento.getX()>=supIzq.getX())&&(Elemento.getX()<=infDer.getX())){
            if ((Elemento.getY()>=supIzq.getY())&&(Elemento.getY()<=infDer.getY())){
                Resp = true;
            }
        }

        return Resp;
    }
    private Posicion Avance(Posicion Jugador, Posicion Destino, int[] Amenazas){
        Posicion Resp;

        double Ang = Jugador.angulo(Destino);
        //Si el más cercano está muy cerca, cambiar la dirección.
        if (Jugador.distancia(Rivales[Amenazas[0]])<7){
            double Desvio = 0.78;
            int Control = 10;
            boolean Encontrado = false;
            double angRival;
            double Dif;
            while ((Control>0) && (!Encontrado)){
                Encontrado = true;
                for (int Bucle = 0;Bucle<Amenazas.length;Bucle++){
                    if (Jugador.distancia(Rivales[Amenazas[Bucle]])<7){
                        angRival = Jugador.angulo(Rivales[Amenazas[Bucle]]);
                        Dif = Math.abs(Ang-angRival);
                        if (Dif<(Desvio/2)){
                            Desvio = Desvio*((angRival>Ang)?-1:1);
                            Ang +=Desvio;
                            Encontrado = false;
                            break;
                        }
                    }
                }
                Control--;
            }
        }
        Resp = Jugador.moverAngulo(Ang,Constantes.ALTURA_CONTROL_BALON);
        return Resp;
    }
    private boolean paseViable(Posicion Jugador, Posicion Destinatario){
        return paseViable(Jugador, Destinatario, false);
    }
    private boolean paseViable(Posicion Jugador, Posicion Destinatario, boolean Avance){
        double DistanciaMIN = (Avance)?0.5:5;
        double DistanciaMAX = 30;
        boolean Resp = false;

        if (Jugador.distancia(Destinatario)<DistanciaMAX){
            if (Jugador.distancia(Destinatario)>DistanciaMIN){
                Resp = this.Libre(Jugador, Destinatario,Rivales);
            }
        }
        return Resp;
    }

    private boolean Libre(Posicion Origen, Posicion Destino){
        return Libre(Origen, Destino, Rivales);
    }
    private boolean Libre(Posicion Origen, Posicion Destino, Posicion[] Rivales){
        boolean Resp = true;
        for (Posicion Rival: Rivales){
            if (!Libre(Origen, Destino, Rival)){
                Resp = false;
                break;
            }
        }
        return Resp;
    }
    private boolean Libre(Posicion Origen, Posicion Destino, Posicion Rival){
        boolean Resp = true;
        double Dist = Origen.distancia(Destino);
        double Ang = Origen.angulo(Destino);
        if ((Origen.distancia(Rival)<=Dist) && (Math.abs(Origen.angulo(Rival)-Ang)<.298)){
            Resp = false;
        }
        return Resp;
    }
	private Posicion puntoEncuentro(Posicion posAntBalon, Posicion Jugador){
        double CoorX,CoorY;
        double Pend = (posAntBalon.getX()==posBalon.getX())?Constantes.LARGO_CAMPO:(posBalon.getY() - posAntBalon.getY()) / (posBalon.getX() - posAntBalon.getX());

        CoorX =(Pend * Jugador.getY() - Pend * posAntBalon.getY() + Pend * Pend * posAntBalon.getX() + Jugador.getX()) / (Pend * Pend + 1.0);
        CoorY = (Pend == 0)?posAntBalon.getY():(Jugador.getY() + ((Jugador.getX() - CoorX) / Pend) + 0.5);
        return new Posicion(CoorX, CoorY);
    }
    private Posicion interceptarBalon(Posicion posAntBalon,int idJugador){
        return interceptarBalon(posAntBalon,idJugador,estadoPase.hayPase());
    }
    private Posicion interceptarBalon(Posicion posAntBalon,int idJugador, boolean irAlEncuentro){
        Posicion Resp = posBalon;
        if (posAntBalon==null) return Resp;

        Posicion Jugador = Propios[idJugador];

        double altMax = (idJugador == Portero)?Constantes.ALTO_ARCO:Constantes.ALTURA_CONTROL_BALON;
        double Ang = posAntBalon.angulo(posBalon);
        double Dist = Jugador.distancia(posBalon);
        double Recorrido = posAntBalon.distancia(posBalon);
        double Velocidad = Constantes.getVelocidad(Detalle.getJugadores()[idJugador].getVelocidad());
        double Factor = (altBalon>0)?Constantes.FACTOR_DISMINUCION_VEL_BALON_AIRE:Constantes.FACTOR_DISMINUCION_VEL_BALON_SUELO;

        if (Dist>2){
            if (altBalon>altMax){
                Resp = Interseccion(posAntBalon, posBalon,Jugador,Jugador.moverAngulo(Ang+((Recorrido*Factor) * Math.sin(Ang) / Velocidad), 1),false);
                //Intentar prevenir que el balón pase entre iteraciones.
                Resp = Resp.moverAngulo(Ang, Recorrido);
            }else{
                if ((Math.abs(posAntBalon.angulo(posBalon)-posBalon.angulo(Jugador))>0.15)||(irAlEncuentro)){
                    Posicion posSigBalon = posBalon;
                    Posicion posSigJugador = Jugador;
                    double altSigBalon = altBalon;
                    double Min;
                    Dist= Constantes.centroArcoInf.norma(Constantes.centroArcoSup);

                    for (int Bucle=100; Bucle>0; Bucle--){
                        posSigBalon = posSigBalon.moverAngulo(Ang, Recorrido*Factor);
                        posSigJugador = posSigJugador.moverAngulo(posSigJugador.angulo(posSigBalon), Velocidad);
                        altSigBalon -= Constantes.G;
                        Min = posSigJugador.norma(posSigBalon);
                        if (altSigBalon<=altMax){
                            if (Min<Dist){
                                Dist = Min;
                                Resp = posSigBalon;
                            }else{
                                //Intentar prevenir que el balón pase entre iteraciones.
                                Resp = posSigBalon;
                                break;
                            }
                        }
                    }
                }else{
                    Resp = Jugador;
                }
            }
        }else{
            Resp = posBalon;
        }
        return Resp;
    }

    private Posicion oportunidadGol(int idJugador){
    //Informa al jugador que tiene una oportunidad razonable del gol.
        Posicion Resp = null;
        //double distMax = Resultados.distMaxExitoGol();
        double distMax = 27;
        double Dist = posBalon.distancia(Constantes.centroArcoSup);

        //Estadísticamente sin oportunidad clara..
        if (Dist>distMax) return Resp;

        double margenError = Constantes.getErrorAngular(Detalle.getJugadores()[idJugador].getPresicion());
        double Ang = posBalon.angulo(Constantes.centroArcoSup);

        //Comprueba si la portería está despejada.
        for (double Bucle=Ang-margenError;Bucle<Ang+margenError;Bucle+=margenError){
            if (this.Libre(posBalon,posBalon.moverAngulo(Ang, Dist))){
                Resp = Constantes.centroArcoSup;
                break;
            }
        }
        //Si la portería está despejada, es oportunidad!
        if (Resp != null) return Resp;

        //Posicion[] cincoPrimeros = Resultados.exitosGol(true,5);
        Posicion[] cincoPrimeros ={
            new Posicion(-17.65087450796063,34.19962375409895),
            new Posicion(5.251568963120055,31.597944229112056),
            new Posicion(13.767473427492328,28.96458610682167),
            new Posicion(-4.731443571194199,32.77574948112405),
            new Posicion(8.314433016038898,33.5551912442679)
        };

        //Comprar la posición actual con tiros a puerta exitosos en condiciones similares.
        if (Dist>20){
        //Bombeo
            double angD, distD;
            Resp = null;
            for (int Bucle=0;Bucle<cincoPrimeros.length;Bucle++){
                Ang = Constantes.centroArcoSup.angulo(cincoPrimeros[Bucle]);
                Dist = Constantes.centroArcoSup.distancia(cincoPrimeros[Bucle]);

                angD = Math.abs(posBalon.angulo(Constantes.centroArcoSup)-Ang);
                distD = Math.abs(posBalon.distancia(Constantes.centroArcoSup)-Dist);

                if ((angD<=margenError)&&(distD<2)){
                    Resp = Constantes.centroArcoSup;
                    break;
                }
            }            
        }else if(Dist<16){
        //Normal
            Posicion Objetivo = Constantes.centroArcoSup;
            Dist = posBalon.distancia(Objetivo);

            boolean esOcacion = true;
            for (double Bucle=Ang-margenError;Bucle<Ang+margenError;Bucle+=margenError){
                if (Interseccion(Constantes.posteDerArcoSup, Constantes.posteIzqArcoSup,posBalon,posBalon.moverAngulo(Bucle, Dist),true) != null){
                    esOcacion = false;
                    break;
                }
            }

            Resp = (esOcacion && this.Libre(posBalon,Objetivo))?Objetivo:null;
        }

        return Resp;
        
    }
    private double fuerzaEstimada(double distReq, int idJugador){
        double Factor = 17.1313914;
        double distPos = Factor+(Detalle.getJugadores()[idJugador].getRemate()*Factor);
        //double Esfuerzo = (posBalon.getY()<0)?Resultados.deficitEstimado(true):Resultados.deficitEstimado(false);
        //TODO: Buscar una forma mejor que poner valores medios cuando Resultados no esté.
        double Esfuerzo = (posBalon.getY()<0)?5:4;
        double Resp = (distReq/distPos)*Esfuerzo;
        Resp = 1;
        return (Resp>1)?1:Resp;
    }
    
    public int[] ordenDinamico(int[] arrayDado) {
        int temp;
        int nroPases = arrayDado.length;
        int[] Prioridad = new int[nroPases];
        int[] valores = new int[nroPases];

        for (int Bucle=0; Bucle<nroPases; Bucle++){
            Prioridad[Bucle] = Bucle;
            valores[Bucle] = arrayDado[Bucle];
        }
        for (int Bucle=0; Bucle<nroPases; Bucle++){
            for (int Bucle2=Bucle+1; Bucle2<nroPases; Bucle2++){
                if (valores[Bucle]<valores[Bucle2]){
                    temp = valores[Bucle2];
                    valores[Bucle2] = valores[Bucle];
                    valores[Bucle] = temp;
                    temp = Prioridad[Bucle2];
                    Prioridad[Bucle2] = Prioridad[Bucle];
                    Prioridad[Bucle] = temp;
                }
            }
        }
        return Prioridad;
    }

    private Posicion Interseccion(Posicion n1, Posicion n2, Posicion n3, Posicion n4, boolean Vectores){
        Posicion Resp = null;
        double  x1=n1.getX(),y1=n1.getY(),
                x2=n2.getX(),y2=n2.getY(),
                x3=n3.getX(),y3=n3.getY(),
                x4=n4.getX(),y4=n4.getY();
        double Delta = (x1-x2)*(y3-y4) - (y1-y2)*(x3-x4);
        double DeltaX = ((x3-x4)*(x1*y2-y1*x2)-(x1-x2)*(x3*y4-y3*x4));
        double DeltaY = ((y3-y4)*(x1*y2-y1*x2)-(y1-y2)*(x3*y4-y3*x4));
        
        Resp = (Delta == 0)?null:new Posicion(DeltaX/Delta,DeltaY/Delta);
        if (Vectores && (Resp != null)){
            if (
                    (Resp.getX() < Math.min(x1,x2) || Resp.getX() > Math.max(x1,x2)) ||
                    (Resp.getX() < Math.min(x3,x4) || Resp.getX() > Math.max(x3,x4))
                    ){
                Resp = null;
            }
        }
        return Resp;
    }

    //Alineaciones.
    Posicion Alineacion[]=new Posicion[]{
        new Posicion(-11.65034965034965,-32.782805429864254),
        new Posicion(-25.44055944055944,-31.119909502262445),
        new Posicion(0.0,-51.31221719457013),
        new Posicion(9.748251748251748,-32.30769230769231),
        new Posicion(25.916083916083913,-32.07013574660634),
        new Posicion(0.0,-12.828054298642533),
        new Posicion(-22.58741258741259,-3.5633484162895925),
        new Posicion(23.538461538461537,-4.276018099547511),
        new Posicion(-19.496503496503497,30.16968325791855),
        new Posicion(0.951048951048951,19.95475113122172),
        new Posicion(17.11888111888112,30.88235294117647)
    };
    Posicion alineacionSacar[]=new Posicion[]{
        new Posicion(-12.363636363636363,-32.30769230769231),
        new Posicion(-26.391608391608393,-31.83257918552036),
        new Posicion(0.0,-51.074660633484164),
        new Posicion(9.748251748251748,-32.30769230769231),
        new Posicion(25.916083916083913,-32.07013574660634),
        new Posicion(-3.804195804195804,-19.004524886877828),
        new Posicion(-22.58741258741259,-3.5633484162895925),
        new Posicion(23.538461538461537,-4.276018099547511),
        new Posicion(-0.4755244755244755,0.0),
        new Posicion(3.3286713286713288,-18.29185520361991),
        new Posicion(5.230769230769231,-0.23755656108597287)
    };
    Posicion alineacionRecibir[]=new Posicion[]{
        new Posicion(-9.034965034965035,-32.30769230769231),
        new Posicion(-26.391608391608393,-31.83257918552036),
        new Posicion(0.0,-51.31221719457013),
        new Posicion(9.748251748251748,-32.30769230769231),
        new Posicion(25.916083916083913,-32.07013574660634),
        new Posicion(-3.3286713286713288,-10.214932126696834),
        new Posicion(-22.58741258741259,-3.5633484162895925),
        new Posicion(23.538461538461537,-4.276018099547511),
        new Posicion(-10.937062937062937,-0.23755656108597287),
        new Posicion(3.090909090909091,-9.264705882352942),
        new Posicion(10.461538461538462,-0.47511312217194573)
    };

    private static final int Portero = 2;
    private static final int[] Centrales = {0,3};
    private static final int[] Laterales = {1,4};
    private static final int MedioCentro = 5;
    private static final int[] Extremos = {6,7};
    private static final int MediaPunta = 9;
    private static final int[] Delanteros = {8,10};

    //private recoleccionInfo evolucionPartido = new recoleccionInfo();
    private ASNFTDetalle Detalle = new ASNFTDetalle();
    private JugadorDetalle[] detRivales;
    private Maniobras estadoManiobra = new Maniobras();
    private chutesVolea estadoVoleas = new chutesVolea();
    private Pase estadoPase = new Pase();
    private Marcajes estadoMarcajes = new Marcajes();
    private LinkedList<Comando> comandos = new LinkedList<Comando>();

    private int enZona = 0;
    private int idPorRival = -1;
    private double varPor = 0;
    private int esperaSaque = 2500/Constantes.ITERACIONES_SAQUE;
    private int contadorAvance = 0;
    private int[] Recuento = {0,0};
    private int[] Pases = new int[11];
    private double altBalon;
    private double Celebra = 0;
    private Posicion posBalon;
    private Posicion[] Propios;
    private Posicion[] Rivales;
    private Posicion[] posAntRivales = new Posicion[11];
    
    public TacticaDetalle getDetalle(){
        return Detalle;
    }
    public Posicion[] getPosicionSaca(SituacionPartido sp){
        return alineacionSacar;
    }
    public Posicion[] getPosicionRecive(SituacionPartido sp){
        Celebra += 0.79;
        for (int Bucle=alineacionRecibir.length-1; Bucle>-1; Bucle--){
            alineacionRecibir[Bucle]=alineacionRecibir[Bucle].moverAngulo(Celebra,0.5);
        }
        return alineacionRecibir;
    }

    public List<Comando> ejecutar(SituacionPartido sp){

        double Dist;
        double Fuerza;
        double Ang;
        boolean Bombeo = false;
        int Destino;
        int idJugador;
        Posicion Jugador;
        Posicion Objetivo;

        int iteracion = sp.iteracion();

        /*
        if (iteracion<(Constantes.ITERACIONES-1)){
            evolucionPartido.guardarInfo(sp);
        }else{
            System.out.println("----------------");
            evolucionPartido.volcarDatos();
        }
        */
        
        //Valores de inicialización única
        if (detRivales == null) detRivales = sp.detalleJugadoresRivales();
        if (idPorRival == -1){
            for (int Bucle = 0;Bucle<11;Bucle++){
                if (detRivales[Bucle].esPortero()){
                    idPorRival = Bucle;
                    break;
                }
            }
        }


        comandos.clear();
        
        boolean golPropio = (sp.golesPropios()>Recuento[0]);
        boolean golRival =  (sp.golesRival()>Recuento[1]);
        Rivales = sp.rivales();
        Propios = sp.misJugadores();
        Recuento[0] = sp.golesPropios();
        Recuento[1] = sp.golesRival();
        int idPase = -1;
        int[] puedenRematar = sp.puedenRematar();
        int[] puedenRematarRival = sp.puedenRematarRival();

        altBalon = sp.alturaBalon();
        Posicion posAntBalon = (posBalon==null)?sp.balon():posBalon;
        posBalon = sp.balon();
        int[] masCercanosPorteria = Constantes.centroArcoInf.indicesMasCercanos(Rivales);
        int propioMasCercanoBalon = posBalon.indiceMasCercano(Propios);
        boolean balonControlado = (Arrays.binarySearch(puedenRematar,propioMasCercanoBalon)>-1);

        if (posAntRivales[idPorRival] != null){
            if (posAntRivales[idPorRival].getY()!=Rivales[idPorRival].getY()){
                varPor++;
            }else if (posAntRivales[idPorRival].getX()!=Rivales[idPorRival].getX()){
                varPor--;
            }
        }
        //Valores a establecer solo al comienzo del partido.
        if (iteracion <2){
            varPor = 0;
        }

        //Ajustar los valores de estadoVoleas
        if (estadoVoleas.enCamino()){
            if (golPropio||golRival){
                estadoVoleas.setExito(golPropio);
            }
        }

        //Gestionar los saques propios y rivales
        //**************************************
        if (sp.saco()){
            //Inicializar el receptor,
            int receptorPase = -1;
            //Elegir quien sacará, cualquiera menos el portero.
            idJugador = (propioMasCercanoBalon==Portero)?posBalon.indiceMasCercano(Propios, Portero):propioMasCercanoBalon;

            //Intentar dar tiempo a los jugadores para colocarse.
            if (esperaSaque<1){
                if (estadoManiobra.Ejecutando()){
                //Hay un plan en marcha...
                    comandos = estadoManiobra.Ejecutar(comandos, puedenRematar,true);
                }else{
                //No hay plan, asumir comportamiento por defecto.
                    //Si es saque de centro...
                    if ((posBalon.getX()==0) && (posBalon.getY()==0)){
                        comandos.add(new ComandoGolpearBalon(Delanteros[0], Propios[MedioCentro], 1, false));
                    }else{
                        if (idJugador==Portero){
                            Destino = (Propios[Portero].distancia(Propios[Extremos[0]])>Propios[Portero].distancia(Propios[Extremos[1]]))?Extremos[1]:Extremos[0];
                            comandos.add(new ComandoGolpearBalon(Portero, Propios[Destino], 1, true));
                        }else{
                            
                            Destino = Constantes.penalSup.indiceMasCercano(Propios, idJugador);
                            if (posBalon.getX()!=9.16){
                            //No es saque de puerta.
                                int[] Cercanos = Propios[idJugador].indicesMasCercanos(Propios, idJugador);
                                Dist = Constantes.centroArcoSup.distancia(Propios[Cercanos[10]]);
                                for (int Bucle=0; Bucle<11; Bucle++){
                                    //Si pasa de 30 metros fuera.
                                    if (Propios[idJugador].distancia(Propios[Bucle])>30) break;

                                    if ((Bucle != idJugador) && (Bucle!=Portero)){
                                        if (Constantes.centroArcoSup.distancia(Propios[Bucle])<Dist){
                                            Destino = Bucle;
                                            Dist = Constantes.centroArcoSup.distancia(Propios[Bucle]);
                                        }

                                    }
                                }
                            }
                            if (Propios[idJugador].distancia(Propios[Destino])>30){
                                Destino = -1;
                            }else{
                                receptorPase = Destino;
                                comandos.add(new ComandoIrA(Destino, Propios[Destino]));
                            }
                            Objetivo = (Destino == -1)?Constantes.penalSup: Propios[Destino];
                            if (Arrays.binarySearch(puedenRematar,idJugador)==-1){
                                comandos.add(new ComandoIrA(idJugador, posBalon));
                            }else{
                                Dist = Propios[idJugador].distancia(Objetivo);
                                Fuerza = fuerzaEstimada(Dist,idJugador);
                                Bombeo = (Dist>20)?true:false;
                                comandos.add(new ComandoGolpearBalon(idJugador, Objetivo, Fuerza, Bombeo));
                            }
                        }
                    }
                }

            }else{
                /**
                 * Elegir un plan de saque, según determinadas condiciones.
                 */
                if (!estadoManiobra.Ejecutando()){
                //Si no hay plan elegido, elegir uno.
                    double absX = Math.abs(posBalon.getX());
                    int Indice = (posBalon.getX()<0)?0:1;
                    estadoManiobra.Limpiar();
                    Objetivo = posBalon;

                    //TODO: Implementar maniobra de saque.
                    /*if (posBalon.getY()<-46){

                     *
                    }else */
                    if ((absX>30) && (posBalon.getY()<-16)){
                    //Zona asignada a los laterales.
                        estadoManiobra.addPaso(Laterales[Indice], Objetivo, Extremos[Indice]);
                        Ang = Objetivo.angulo(Alineacion[Extremos[Indice]]);
                        Dist = 30;
                        Objetivo = Objetivo.moverAngulo(Ang, Dist);
                        estadoManiobra.addPaso(Extremos[Indice], Objetivo, Delanteros[Indice]);
                        Ang = Objetivo.angulo(Alineacion[Delanteros[Indice]]);
                        Dist = 30;
                        Objetivo = Objetivo.moverAngulo(Ang, Dist);
                        estadoManiobra.addPaso(Delanteros[Indice], Objetivo, MediaPunta);
                        Ang = Objetivo.angulo(Constantes.penalSup);
                        Dist = 30;
                        Objetivo = Objetivo.moverAngulo(Ang, Dist);
                        estadoManiobra.addPaso(MediaPunta, Objetivo,MediaPunta);
                    }else if ((absX>30) && (posBalon.getY()>=-16) && (posBalon.getY()<16)){
                    //Zona asignada a los extremos.
                        estadoManiobra.addPaso(Extremos[Indice], Objetivo, Delanteros[Indice]);
                        Ang = Objetivo.angulo(Alineacion[Delanteros[Indice]]);
                        Dist = 30;
                        Objetivo = Objetivo.moverAngulo(Ang, Dist);
                        estadoManiobra.addPaso(Delanteros[Indice], Objetivo, MediaPunta);
                        Ang = Objetivo.angulo(Constantes.penalSup);
                        Dist = 30;
                        Objetivo = Objetivo.moverAngulo(Ang, Dist);
                        estadoManiobra.addPaso(MediaPunta, Objetivo,MediaPunta);

                    }else{
                    //No se ajusta a ningún plan, usar solución por defecto.
                        //Colocar a los jugadores en posición.
                        boolean enPosicion = true;
                        for (int Bucle = 0; Bucle<11; Bucle++){
                            if (Bucle != receptorPase){
                                //Asignar posiciones a los jugadores.
                                if (Bucle != idJugador){
                                    Objetivo = Alineacion[Bucle];
                                }else{
                                    Objetivo = posBalon;
                                }
                                //Si encontramos uno fuera de posición, poner a falso la bandera.
                                if (Objetivo.distancia(Propios[Bucle])>0.1){
                                    if (enPosicion) enPosicion = false;
                                    comandos.add(new ComandoIrA(Bucle, Objetivo));
                                }
                            }
                        }
                        //Si todos están en posición, dejar la cuenta atrás.
                        if (enPosicion) esperaSaque=0;
                    }
                }
                if (estadoManiobra.nroPasos > 0){
                    comandos = estadoManiobra.Ejecutar(comandos, puedenRematar,false);
                }
                /**
                 * Dar tiempo a los jugadores de intentar tomar las posiciones del plan
                 * que se vaya a ejecutar. Si el tiempo se hacerca al límite, o ya los
                 * jugadores están en posición, hacer el saque.
                 */
                esperaSaque--;
                if ((esperaSaque<1)||(estadoManiobra.enPosicion())){
                    esperaSaque = 0;
                }
            }
            return comandos;
        }else if (sp.sacaRival()){
        //Gestonar los saques del rival.

            //Si hay una maniobra en marcha, es que tardó mucho y perdimos el saque.
            if (estadoManiobra.Ejecutando()){
                estadoManiobra.Limpiar();
            }
            //Portero a su puesto.
            comandos.add(new ComandoIrA(Portero, Alineacion[Portero]));
            //Si da tiempo, cubrir a los mas cercanos.
            int[] Receptores = posBalon.indicesMasCercanos(Rivales);
            int jugadorMasCercano;
            for (int Receptor=1;Receptor<6;Receptor++){
                jugadorMasCercano = Rivales[Receptores[Receptor]].indiceMasCercano(Propios);
                if (jugadorMasCercano != Portero){
                    Ang = Rivales[Receptores[Receptor]].angulo(posBalon);
                    comandos.add(new ComandoIrA(jugadorMasCercano, Rivales[Receptores[Receptor]].moverAngulo(Ang, 2)));
                }
            }
            return comandos;
        }else{
            //Ajustar el tiempo de espera al saque cuando no sacamos.
            if (esperaSaque<1){
                estadoManiobra.Limpiar();
                esperaSaque = 5000/Constantes.ITERACIONES_SAQUE;
            }
        }
        

        //Si el balón sale de la zona de peligro, resetear enZona.
        if (posBalon.getY()>-20){
            enZona=0;
        }

        //Fallos en pases y voleas
        if (estadoVoleas.enCamino()){
            if (puedenRematarRival.length>0){
                estadoVoleas.setExito(false);
            }
        }

        //Gestionar si nadie va a por la pelota.
        if ((posAntBalon != null)&&(iteracion>1)){
            if (posAntBalon.distancia(posBalon)<0.01){
                comandos.add(new ComandoIrA(propioMasCercanoBalon, posBalon));
                comandos.add(new ComandoGolpearBalon(propioMasCercanoBalon, Constantes.centroArcoSup, 1, true));
                return comandos;
            }
        }

        //Fallos en pases
        if (estadoPase.hayPase()){
            if (Propios[estadoPase.idDestinatario].distancia(posBalon)>Propios[estadoPase.idDestinatario].distancia(posAntBalon)){
                estadoPase.paseKO();
            }
        }
        
        boolean esMasCercanoBalon;
        boolean fueraLimites;

        //Portero(3).
        //***********
        Jugador = Propios[Portero];
        idJugador = Portero;
        esMasCercanoBalon = (propioMasCercanoBalon==idJugador);
        fueraLimites = false;
        if (Jugador.distancia(Alineacion[Portero])>20){
            if (Jugador.distancia(posBalon)<3){
                if (!esMasCercanoBalon){
                    fueraLimites = true;
                }
            }else{
                fueraLimites = true;
            }
        }

        //Establecer la posición del portero.
        //double Separacion = Resultados.estPortero(3);
        double Separacion;
        if ((posBalon.getY()<0)||(fueraLimites)){
            Separacion = (Jugador.distancia(posBalon)*Constantes.ANCHO_AREA_GRANDE*0.5)/30;
        }else{
            //TODO: Buscar en las estadísticas el valor adecuado...
            Separacion = (Jugador.distancia(posBalon)*Constantes.ANCHO_AREA_GRANDE*0.5)/30;
        }
        Separacion = (Separacion>Constantes.ANCHO_AREA_GRANDE*0.5)?Constantes.ANCHO_AREA_GRANDE*0.5:Separacion;
        Objetivo = Constantes.centroArcoInf.moverAngulo(Constantes.centroArcoInf.angulo(posBalon), Separacion);
        int idRivalConBalon = posBalon.indiceMasCercano(Rivales);
        //Objetivo = Resultados.accionPortero(posAntBalon,idRivalConBalon,Objetivo);
        //TODO: Comprobar booleanos contra estadística
        boolean accionRequerida = (
                (
                    (posBalon.getY()<Propios[Centrales[0]].getY()) &&
                    (posBalon.getY()<Propios[Centrales[1]].getY()) &&
                    (Constantes.centroArcoInf.distancia(posBalon)<30)
                    ) ||
                    (Constantes.centroArcoInf.distancia(posBalon)<Constantes.ANCHO_AREA_GRANDE)
                );
        boolean enAproximacion = (
                        (Math.abs(posAntBalon.angulo(Jugador)-posAntBalon.angulo(posBalon))<0.7)||
                        (Jugador.distancia(posBalon)<Rivales[posBalon.indiceMasCercano(Rivales)].distancia(posBalon))
                    );
        boolean noControlable = (altBalon>Constantes.ALTO_ARCO);
        boolean roboSeguro = (Rivales[idRivalConBalon].distancia(posBalon)>posBalon.distancia(Jugador));
        boolean fueraLinea = (Objetivo.getY()<-52);
        //Estructura IF emulando la tabla de la clase.
        if (accionRequerida){
            //Si han tirado a puerta o se acercan peligrosamente...
            if (enAproximacion){
                //Si no es controlable.
                if (noControlable){
                    //Equivalente a 111 en estadística.
                    Ang = posAntBalon.angulo(posBalon);
                    Objetivo = this.Interseccion(posAntBalon, posBalon, Constantes.posteDerArcoInf, Constantes.posteIzqArcoInf,false);
                    if (Objetivo == null){
                        Objetivo = Alineacion[idJugador];
                    }else{
                        Objetivo = Objetivo.moverAngulo((Ang*-1), Constantes.DISTANCIA_CONTROL_BALON_PORTERO*0.8);
                    }
                }else{
                //Caso contrario, interceptarlo.
                    if(roboSeguro){
                        //Equivalente a 1101 en estadística.
                        Objetivo = this.interceptarBalon(posAntBalon, idJugador);
                    }else{
                        //Equivalente a 1100 en estadística.
                        Ang = Constantes.centroArcoInf.angulo(posBalon);
                        Dist = Constantes.centroArcoInf.distancia(posBalon);
                        //TODO: No es exacto a la estadística, buscar aproximación mejor.
                        Dist = (Dist>Constantes.ANCHO_AREA_CHICA/2)?Constantes.ANCHO_AREA_CHICA/2:Dist;
                        Objetivo = this.Interseccion(posAntBalon, posBalon, Constantes.posteDerArcoInf, Constantes.posteIzqArcoInf,true);
                        if (Objetivo == null){
                        //Si no va a tomar posición defensiva
                            Objetivo = Constantes.centroArcoInf.moverAngulo(Ang, Dist/2);
                        }else{
                            //No pegarse demasiado a la línea de gol.
                            Objetivo = Objetivo.moverAngulo(Ang, Constantes.DISTANCIA_CONTROL_BALON_PORTERO*0.5);
                        }
                    }
                }
            }else{
                //Equivalente a 10 en estadística.
                Objetivo = Constantes.centroArcoInf.moverAngulo(Constantes.centroArcoInf.angulo(posBalon), Separacion);
            }
            if (fueraLinea){
                //Objetivo = new Posicion(Objetivo.getX(), -52.4);
                //TODO: Pasar a función
                Ang = Constantes.centroArcoInf.angulo(posBalon);
                Dist = Constantes.centroArcoInf.distancia(posBalon);
                //TODO: No es exacto a la estadística, buscar aproximación mejor.
                Dist = (Dist>Constantes.ANCHO_AREA_CHICA/2)?Constantes.ANCHO_AREA_CHICA/2:Dist;
                Objetivo = this.Interseccion(posAntBalon, posBalon, Constantes.posteDerArcoInf, Constantes.posteIzqArcoInf,false);
                if (Objetivo == null){
                //Si no va a tomar posición defensiva
                    Objetivo = Constantes.centroArcoInf.moverAngulo(Ang, Dist/2);
                }else{
                    //No pegarse demasiado a la línea de gol.
                    Objetivo = Objetivo.moverAngulo(Ang, Constantes.DISTANCIA_CONTROL_BALON_PORTERO*0.2);
                }

            }
        }

        //Se aleja mucho de su puesto.
        if ((Jugador.distancia(Alineacion[0])>25)||(Jugador.distancia(Objetivo)>25)){
            Objetivo = Alineacion[idJugador];
        }
        //No mandarle a moverse al mismo sitio.
        if (!Jugador.equals(Objetivo)){
            comandos.add(new ComandoIrA(idJugador,Objetivo));
        }


        if (Arrays.binarySearch(puedenRematar,idJugador)>-1){
        //El portero debe patear...
            if (estadoPase.idDestinatario==idJugador) estadoPase.paseOK();  //El pase se recibió
            //Evijar jugar al frontón con los rivales.
            enZona += (posBalon.getY()<=Constantes.penalInf.getY())?1:0;
            if ((enZona<2) && (Rivales[masCercanosPorteria[0]].distancia(Constantes.centroArcoInf)>30)){
                
                Arrays.fill(Pases, -1);
                Pases[0]=Laterales[0];
                Pases[1]=Laterales[1];
                Pases[2]=MedioCentro;

                idPase = estadoPase.seleccionaPase(idJugador, Pases);
                Destino = (idPase>-1)?Pases[idPase]:-1;

            }else{
            //El portero está asediado.
                //Por defecto despejar al centro del campo.
                Destino = -1;

                //Si no hay rivales a menos de cuatro metros...
                if(Rivales[idRivalConBalon].distancia(posBalon)>4){
                    //Seleccionar receptores que estén a mas de 20m
                    for (int Bucle=0; Bucle<Propios.length; Bucle++){
                        if (Jugador.distancia(Propios[Bucle])>20){
                            if (this.Libre(Jugador, Propios[Bucle])){
                                Destino = Bucle;
                                idPase = -1;
                                break;
                            }
                        }
                    }
                }
            }
            //Si hay pase seleccionado
            if (Destino != -1){
                estadoPase.addPase(Destino, idPase, idJugador);
                Objetivo = Propios[Destino];
            }else{
                //El despeje, por defecto al centro del campo
                Objetivo = Constantes.centroArcoSup;
                //Buscar un ángulo de tiro que no esté cubierto por un rival, aunque vaya fuera.
                for (double Bucle=Math.PI-0.39; Bucle>0.39; Bucle-=0.79){
                    if (this.Libre(Jugador, Jugador.moverAngulo(Bucle, 4))){
                        Objetivo = Jugador.moverAngulo(Bucle, Constantes.LARGO_CAMPO_JUEGO);
                        break;
                    }
                }
            }
            Dist = Jugador.distancia(Objetivo);
            Fuerza = fuerzaEstimada(Dist,idJugador);
            Bombeo = (Dist>20)?true:false;
            comandos.add(new ComandoGolpearBalon(idJugador, Objetivo, Fuerza, Bombeo));
        }

        //Centrales (1,4)
        //***************
        Posicion rivalMarcado = null;
        for (int Bucle=0; Bucle<Centrales.length; Bucle++){
            idJugador = Centrales[Bucle];
            Jugador = Propios[idJugador];
            rivalMarcado = estadoMarcajes.getMarcaje(idJugador);

            //Si el balón está en su área...
            if (
                    (
                        (Math.abs(posAntBalon.angulo(posBalon)-posBalon.angulo(Jugador))<0.39) &&
                        (Jugador.distancia(posBalon)<40) && (!balonControlado)
                    )||
                    (propioMasCercanoBalon==idJugador)
                ){
                //Interceptar el balón.
                Objetivo = this.interceptarBalon(posAntBalon, idJugador,true);
                //Notificar la finalización del marcaje.
                estadoMarcajes.delMarcaje(idJugador);
            }else{
            //Si el balón no está en el área, marcar al rival más cercano.
                //Si tiene rival asignado y está todavía en la zona, marcarlo.
                if ((estadoMarcajes.estaMarcando(idJugador))&&(this.enArea(Alineacion[idJugador],22,42,rivalMarcado))){
                        Objetivo = rivalMarcado;//estadoMarcajes.getMarcaje(idJugador);
                }else{
                //Caso contrario, buscar un marcaje.
                    //Limpiar cualquier posible marcaje anterior.
                    estadoMarcajes.delMarcaje(idJugador);
                    Posicion Rival = null;
                    int[] rivalesMasCercanos = Jugador.indicesMasCercanos(Rivales);
                    for (int idRival=0;idRival<rivalesMasCercanos.length;idRival++){
                        Rival = Rivales[idRival];
                        if (
                                (this.enArea(Alineacion[idJugador],22,42,Rival)) &&
                                (estadoMarcajes.estaMarcado(idRival)<0)
                            ){
                            estadoMarcajes.addMarcaje(idJugador, idRival);
                            Objetivo = estadoMarcajes.getMarcaje(idJugador);
                            break;
                        }
                    }
                    //Si no se encontró un objetivo de marcaje, ir a su posición.
                    if (!estadoMarcajes.estaMarcando(idJugador)){
                        Objetivo = Alineacion[idJugador];
                    }
                }
            }
            if (!Jugador.equals(Objetivo)){
                comandos.add(new ComandoIrA(idJugador,Objetivo));
            }

            //Pase.
            if (Arrays.binarySearch(puedenRematar,idJugador)>-1){
                if (estadoPase.idDestinatario==idJugador) estadoPase.paseOK();  //El pase se recibió

                //Destino = Resultados.selPaseEst(idJugador);
                Destino = -1;

                if (Destino != -1){
                    estadoPase.addPase(Destino, idPase, idJugador);
                    Objetivo = Propios[Destino];
                }else{
                    Objetivo = Propios[(Jugador.distancia(Propios[Delanteros[0]])>Jugador.distancia(Propios[Delanteros[1]]))?Delanteros[1]:Delanteros[0]];
                }
                Dist = Jugador.distancia(Objetivo);
                Fuerza = fuerzaEstimada(Dist,idJugador);
                Bombeo = (Dist>20)?true:false;
                comandos.add(new ComandoGolpearBalon(idJugador, Objetivo, Fuerza, Bombeo));
            }

        }

        //Laterales (2,5)
        //***************
        for (int Bucle=0;Bucle<Laterales.length;Bucle++){
            idJugador = Laterales[Bucle];
            Jugador = Propios[idJugador];
            if (posBalon.getY()>0){
                Objetivo = Alineacion[idJugador];
            }else{
                if (
                        (
                            (Math.abs(posAntBalon.angulo(posBalon)-posBalon.angulo(Jugador))<0.78) && 
                            (Jugador.distancia(posBalon)<39) && (!balonControlado)
                        )||
                        (propioMasCercanoBalon==idJugador)
                    ){
                    //Interceptar el balón.
                    Objetivo = this.interceptarBalon(posAntBalon, idJugador,true);

                }else{
                    if (Jugador.distancia(posBalon)<6){
                        Objetivo = posBalon;
                    }else{
                        if (Jugador.getY()<-33){
                            Objetivo = (Bucle==0)?Constantes.posteIzqArcoInf:Constantes.posteDerArcoInf;
                        }else{
                            Objetivo = new Posicion(Alineacion[idJugador].getX(),posBalon.getY());
                        }
                    }
                }
            }
            if (!Jugador.equals(Objetivo)){
                comandos.add(new ComandoIrA(idJugador,Objetivo));
            }
            
            //Pase.
            if (Arrays.binarySearch(puedenRematar,idJugador)>-1){
                if (estadoPase.idDestinatario==idJugador) estadoPase.paseOK();  //El pase se recibió
                //Si estamos apoyando la portería, despeje!!!
                if (Jugador.getY()<-33){
                    Destino = -1;
                }else{

                    
                    Arrays.fill(Pases, -1);
                    Pases[0]=(idJugador==Laterales[0])?Extremos[0]:Extremos[1];
                    Pases[1]=MedioCentro;
                    Pases[2]=(idJugador==Laterales[0])?Centrales[0]:Centrales[1];

                    idPase = estadoPase.seleccionaPase(idJugador, Pases);
                    Destino = (idPase>-1)?Pases[idPase]:-1;
                    
                }
               if (Destino != -1){
                    estadoPase.addPase(Destino, idPase, idJugador);
                    Objetivo = Propios[Destino];
                }else{
                    Objetivo = Constantes.centroArcoSup;
                }
                Dist = Jugador.distancia(Objetivo);
                Fuerza = fuerzaEstimada(Dist,idJugador);
                Bombeo = (Dist>20)?true:false;
                comandos.add(new ComandoGolpearBalon(idJugador, Objetivo, Fuerza, Bombeo));
            }
        }

        //MedioCentro (6)
        //***********
        idJugador = MedioCentro;
        Jugador = Propios[MedioCentro];
        
        //Si el balón está a su alcance, a por él.
        Ang = posAntBalon.angulo(posBalon);
        if (
            (propioMasCercanoBalon==idJugador)||
            (
                (!balonControlado) &&
                ((Ang<0)&&(posAntBalon.getY()<9)) &&
                (Jugador.distancia(posBalon)<40)
            )
        ){
            //Interceptar el balón.
            Objetivo = this.interceptarBalon(posAntBalon, idJugador,true);
        }else if(posBalon.getY()>0){
        //Si no lo tiene cerca, colocarse para facilitar el pase.
            Ang = Alineacion[idJugador].angulo(posBalon);
            Dist = Jugador.distancia(posBalon)*0.5;
            Objetivo = Alineacion[idJugador].moverAngulo(Ang,15);
        }else{
            //Si sale de la zona, por defecto, volver a la posición.
            Objetivo = Alineacion[idJugador];
            //Si se acerca el balón...
            if (posBalon.getY()<20){
                //Apoyar.
                Objetivo = new Posicion(posBalon.getX(),3);
            }
        }
        if (!Jugador.equals(Objetivo)){
            comandos.add(new ComandoIrA(idJugador,Objetivo));
        }


        //Pase.
        if ((Arrays.binarySearch(puedenRematar,idJugador)>-1)){
            if (estadoPase.idDestinatario==idJugador) estadoPase.paseOK();  

            //Destino = Resultados.selPaseEst(idJugador);
            Destino = -1;

            if (Destino != -1){
                estadoPase.addPase(Destino, idPase, idJugador);
                Objetivo = Propios[Destino];
            }else{
                Objetivo = Propios[Constantes.centroArcoSup.indiceMasCercano(Propios,idJugador)];
            }
            Dist = Jugador.distancia(Objetivo);
            Fuerza = fuerzaEstimada(Dist,idJugador);
            Bombeo = (Dist>20)?true:false;
            comandos.add(new ComandoGolpearBalon(idJugador, Objetivo, Fuerza, Bombeo));
        }

        //Extremos (7,8)
        //**************
        for (int Bucle=0; Bucle<Extremos.length; Bucle++){
            idJugador = Extremos[Bucle];
            Jugador = Propios[idJugador];
            if (posBalon.getY()>0){
            //Si estamos atacando...
                //Desplazar la posición en la zona rival.
                double CoorX = Alineacion[idJugador].getX();
                double CoorY = Alineacion[idJugador].getY()+18;
                CoorY = (CoorY>51)?51:CoorY;
                Posicion posAtaque = new Posicion(CoorX,CoorY);
                if ((posAtaque.distancia(posBalon)<16)||(propioMasCercanoBalon==idJugador)){
                    Objetivo = posBalon;
                }else{
                    CoorY = posBalon.getY()+8;
                    CoorY = (CoorY>51)?51:CoorY;
                    CoorX = 22-(CoorY*0.14);
                    Objetivo = new Posicion((CoorX*((Bucle==0)?-1:1)),CoorY);
                }
            }else{
                if (
                        (estadoPase.idDestinatario==idJugador)||
                        (
                            (propioMasCercanoBalon == idJugador) &&
                            (estadoPase.idRemitente!=idJugador)
                            )
                        ){

                    Objetivo = this.interceptarBalon(posAntBalon, idJugador);
                }else{
                    Objetivo = Alineacion[idJugador];
                }
            }
            if (!Jugador.equals(Objetivo)){
                comandos.add(new ComandoIrA(idJugador,Objetivo));
            }

            //Pase.
            if ((Arrays.binarySearch(puedenRematar,idJugador)>-1) && (estadoPase.idDestinatario==idJugador)) estadoPase.paseOK();  //El pase se recibió
            if ((Arrays.binarySearch(puedenRematar,idJugador)>-1)
                    && (Propios[MedioCentro].distancia(posBalon)>Constantes.DISTANCIA_CONTROL_BALON*2)
                    && (Propios[MediaPunta].distancia(posBalon)>Constantes.DISTANCIA_CONTROL_BALON*2)
                ){
                Objetivo = null;
                Fuerza = 0;
                Destino = -1;
                //Comprobar si el jugador tiene oportunidad de gol.
                Objetivo = oportunidadGol(idJugador);
                if (Objetivo == null){
                //Si él no tiene, tal vez otro la tenga.
                    int[] cercanosPorteria = Constantes.centroArcoSup.indicesMasCercanos(Propios, idJugador);
                    for (int idCercano=0;idCercano<cercanosPorteria.length;idCercano++){
                        if (Constantes.centroArcoSup.distancia(Propios[cercanosPorteria[idCercano]])>27) break;
                        Objetivo = oportunidadGol(cercanosPorteria[idCercano]);
                        //TODO: Mirar que sea viable el pase.
                        if (Objetivo != null){
                            Destino = cercanosPorteria[idCercano];
                            break;
                        }
                    }
                }else{
                    Destino = -2;
                    if (posBalon.getY()>0){
                        if (Destino<0){
                            //Primera opción, si es posible, avanzar por la banda.
                            double CoorY = Constantes.penalSup.getY();
                            double CoorX = 23*((posBalon.getX()<0)?-1:1);
                            Objetivo = new Posicion(CoorX,CoorY);
                            if (paseViable(Jugador,Objetivo,true)){
                                Objetivo = Avance(Jugador,Objetivo,Jugador.indicesMasCercanos(Rivales));
                                Fuerza = .4;
                                contadorAvance++;
                            }else{
                                contadorAvance=0;
                                Destino = -1;
                            }
                            /*Si avanzando ha llegado a la línea de 30 o
                             *ha chupa'o demasiado balón, buscar el delantero
                             *más cerano.
                             */
                            if ((posBalon.getY()>30)||(contadorAvance>5)){
                                contadorAvance=0;
                            }
                        }
                    }else{
                        Destino = -1;
                    }
                }

                if (Destino == -2){
                    Dist = Jugador.distancia(Objetivo);
                    Fuerza = (Fuerza==0)?fuerzaEstimada(Dist,idJugador):Fuerza;
                    comandos.add(new ComandoGolpearBalon(idJugador, Objetivo, Fuerza, (Dist>29)));
                }else{
                    if (Destino != -1){
                        estadoPase.addPase(Destino, idPase, idJugador);
                        Objetivo = Propios[Destino];
                    }else{
                        Objetivo = Constantes.centroArcoSup;
                    }
                    Dist = Jugador.distancia(Objetivo);
                    Fuerza = fuerzaEstimada(Dist,idJugador);
                    comandos.add(new ComandoGolpearBalon(idJugador, Objetivo, Fuerza, (Dist>29)));
                }
            }
        }


        //MediaPunta (10)
        //***************
        idJugador = MediaPunta;
        Jugador = Propios[MediaPunta];

        if (posBalon.getY()<0){
        //La pelota en nuestro campo.
            //Si es el destinatario de un pase, o es el más cercano.
            if (
                    (estadoPase.idDestinatario==idJugador)||
                    (
                        (propioMasCercanoBalon == idJugador) &&
                        (estadoPase.idRemitente!=idJugador)
                        )
                    ){
                Objetivo = this.interceptarBalon(posAntBalon, idJugador);
            }else{
            //Si está muy lejos, ofrecerse para pase.
                Objetivo = posBalon.moverAngulo(posBalon.angulo(Constantes.centroArcoSup), 15);
            }
        }else{
            if (Jugador.distancia(posBalon)<15){
                Objetivo = posBalon;
            }else{
            //Si no tiene acceso al balón, intentar apoyar al que lo tenga.
                int paraApoyar = -1;
                //Comprobar si el balón lo tienen los extremos o el medio centro.
                for (int Bucle=0;Bucle<2;Bucle++){
                    if (Propios[Extremos[Bucle]].distancia()<2){
                        paraApoyar = Extremos[Bucle];
                    }
                }
                if (Propios[MedioCentro].distancia()<2){
                    paraApoyar = MedioCentro;
                }
                if (paraApoyar != -1){
                //Apoyar al que tiene el balón.
                    Objetivo = new Posicion(posBalon.getX(),17);
                }else{
                //Comprobar si lo tienen los delanteros.
                    paraApoyar = -1;
                    for (int Bucle=0;Bucle<2;Bucle++){
                        if (Propios[Delanteros[Bucle]].distancia()<2){
                            paraApoyar = Delanteros[Bucle];
                        }
                    }
                    if (paraApoyar != -1){
                        Objetivo = new Posicion(Constantes.centroArcoSup.getX(),42);
                    }else{
                        Objetivo = Alineacion[idJugador];
                    }
                }
            }
        }
        if (!Jugador.equals(Objetivo)){
            comandos.add(new ComandoIrA(idJugador,Objetivo));
        }
        if (Arrays.binarySearch(puedenRematar,idJugador)>-1){
            if (estadoPase.idDestinatario==idJugador){
                estadoPase.paseOK();
            }  //El pase se recibió

            Fuerza = 0;
            Destino = -1;
            if (posBalon.getY()<0){
            //La pelota en nuestro campo.
                //Destino = Resultados.selPaseEst(idJugador);
                Destino = -1;
            }else{
                //Si estamos cerca de la portería...
                Objetivo = oportunidadGol(idJugador);
                if (Objetivo == null){
                    //Objetivo primario, el delantero más cercano.
                    Destino = (Jugador.distancia(Propios[Delanteros[0]])>Jugador.distancia(Propios[Delanteros[1]]))?Delanteros[1]:Delanteros[0];
                    if (!paseViable(Jugador,Propios[Destino])){
                        //Avanzar con la pelota en dirección a la portería.
                        Objetivo = Avance(Jugador,Constantes.centroArcoSup,Jugador.indicesMasCercanos(Rivales));
                        Fuerza = .4;
                        if (!paseViable(Jugador,Objetivo)){
                            Fuerza = 0;
                            Destino = -1;
                        }
                        contadorAvance++;
                    }
                }else{
                    Fuerza = this.fuerzaEstimada(Jugador.distancia(Objetivo),idJugador);
                }
            }

            if (Fuerza == 0){
                if (Destino != -1){
                    estadoPase.addPase(Destino, idPase, idJugador);
                    Objetivo = Propios[Destino];
                }else{
                    Objetivo = Constantes.centroArcoSup;
                }
                Dist = Jugador.distancia(Objetivo);
                Fuerza = this.fuerzaEstimada(Dist, idJugador);
            }else{
                Dist = Jugador.distancia(Objetivo);
            }
            Bombeo = (Dist>18)?true:false;
            comandos.add(new ComandoGolpearBalon(idJugador, Objetivo, Fuerza, Bombeo));
        }

        //Delanteros (9,11)
        //***********
        int[][] AreaDelanteros = {{-20,15,-10,36},{10,15,19,36}};
        for (int Bucle=0;Bucle<Delanteros.length;Bucle++){
            idJugador = Delanteros[Bucle];
            Jugador = Propios[idJugador];
            Posicion otroDelantero = Propios[Delanteros[1^0^Bucle]];


            //Si es el jugador más cercano a la pelota, o es el receptor de un pase.
            if (
                    (propioMasCercanoBalon == idJugador)||
                    (estadoPase.idDestinatario==idJugador)
                    ){
                    Objetivo = this.interceptarBalon(posAntBalon, idJugador);
            }else{
                if (posBalon.getY()<0){
                //Balón en nuestro campo.
                    //Si el mediapunta espera un pase...
                    if (estadoPase.idDestinatario==MediaPunta){
                        Objetivo = Alineacion[idJugador];
                    }else{
                        //Si el balón está en su área de responsabilidad...
                        if (
                                (
                                    (Math.abs(posAntBalon.angulo(posBalon)-posBalon.angulo(Jugador))<0.39) &&
                                    (Jugador.distancia(posBalon)<40) && (!balonControlado)
                                )||
                                (propioMasCercanoBalon==idJugador)
                            ){
                            Objetivo = this.interceptarBalon(posAntBalon, idJugador);
                        }else{
                            //Cubrir su zona.
                            double CoorY = AreaDelanteros[Bucle][1]+((AreaDelanteros[Bucle][3]-AreaDelanteros[Bucle][1])/2);
                            double CoorX = AreaDelanteros[Bucle][0]+((AreaDelanteros[Bucle][2] - AreaDelanteros[Bucle][0])/2);
                            Objetivo = new Posicion(CoorX,CoorY);
                        }
                    }
                }else{
                //Balón en el campo rival.
                    Ang = Constantes.centroArcoSup.angulo(posBalon);
                    Dist = Constantes.centroArcoSup.distancia(Constantes.penalSup);
                    Posicion Desmarque = Constantes.centroArcoSup.moverAngulo(Ang, Dist);

                    //Si el mediapunta viene con el balón, o está recibiendo un pase...
                    if (
                            (estadoPase.idDestinatario==MediaPunta) ||
                            (Propios[MediaPunta].distancia(posBalon)<=(Constantes.DISTANCIA_CONTROL_BALON))
                            ){
                        //Desmarcarse.
                        Objetivo = Desmarque;

                        //Si este delantero es el más lejano, ofrecerse al balón.
                        if (otroDelantero.distancia(posBalon)<Jugador.distancia(posBalon)){
                            //Si el balón está fuera de su zona, colocarse.
                            if (posBalon.getY()>20){
                                //Si no le han pasado a otro, o es el más cercano...
                                if (
                                        (propioMasCercanoBalon == idJugador) ||
                                        (!estadoPase.hayPase())
                                    ){
                                    Objetivo = this.interceptarBalon(posAntBalon, idJugador);
                                }
                            }
                        }
                    }else{
                        //Si lo trae un extremo...
                        double[] DistExtremo ={
                            Propios[Extremos[0]].distancia(posBalon),
                            Propios[Extremos[1]].distancia(posBalon)
                        };
                        if (
                                (DistExtremo[0]<=Constantes.DISTANCIA_CONTROL_BALON)||
                                (DistExtremo[1]<=Constantes.DISTANCIA_CONTROL_BALON)||
                                (estadoPase.idDestinatario==Extremos[0])||
                                (estadoPase.idDestinatario==Extremos[1])
                            ){
                            Posicion Extremo = Propios[Extremos[(DistExtremo[0]>DistExtremo[1])?1:0]];
                            if (Extremo.getY()<25){
                            //Ofrecerse.
                                Objetivo= new Posicion(posBalon.getX(),40);
                            }else{
                            //Desmarcarse.
                                Objetivo = Desmarque;
                            }
                        }else{

                            if (
                                    (Jugador.distancia(posBalon)<30) &&
                                    (otroDelantero.norma(posBalon)>Jugador.norma(posBalon))
                                    ){
                                Objetivo = this.interceptarBalon(posAntBalon, idJugador);
                            }else{
                            //Desmarcarse.
                                if (otroDelantero.getY()>25){
                                    //Ofrecerse para pase.
                                    Ang = Constantes.centroArcoSup.angulo(posBalon);
                                    if (Jugador.getY()>25){
                                        Ang = 1.57+(1.57-Constantes.centroArcoSup.angulo(otroDelantero));
                                    }
                                    Dist = Constantes.centroArcoSup.distancia(Constantes.penalSup);
                                    Objetivo = Constantes.centroArcoSup.moverAngulo(Ang, Dist);
                                }else{
                                    Objetivo = Desmarque;
                                }
                            }
                        }
                    }
                }
            }//Si no es el más cercano o receptor de un pase.
            if (!Jugador.equals(Objetivo)){
                comandos.add(new ComandoIrA(idJugador,Objetivo));
            }

            //Pase.            
            if ((Arrays.binarySearch(puedenRematar,idJugador)>-1)){
                //Si se recibió un pase, notificarlo.
                if (estadoPase.idDestinatario==idJugador) estadoPase.paseOK();
                //Dar al MedioCentro prioridad sobre el balón.

                if (Propios[MedioCentro].distancia(posBalon)>2){
                    Fuerza = 0;
                    Destino = -1;
                    Dist = Jugador.distancia(Constantes.centroArcoSup);
                    //Comprobar si el jugador tiene oportunidad de gol.
                    Objetivo = oportunidadGol(idJugador);
                    if (Objetivo == null){
                    //Si él no tiene, tal vez otro la tenga.
                        int[] cercanosPorteria = Constantes.centroArcoSup.indicesMasCercanos(Propios, idJugador);
                        for (int idCercano=0;idCercano<cercanosPorteria.length;idCercano++){
                            if (Constantes.centroArcoSup.distancia(Propios[cercanosPorteria[idCercano]])>27) break;
                            Objetivo = oportunidadGol(cercanosPorteria[idCercano]);
                            if ((Objetivo != null)&&(Libre(Jugador,Objetivo))){
                                Destino = cercanosPorteria[idCercano];
                                break;
                            }
                        }
                    }
                    if (Objetivo == null){
                        //Si estamos sacando...
                        if ((posBalon.getX()==0) && (posBalon.getY()==0)){
                            Objetivo = Propios[MediaPunta];
                            Fuerza = .8;
                            estadoPase.addPase(MediaPunta, -1, -1);
                        }else{
                            //Si estamos cerca de la portería...
                            if (Dist<15){
                                Destino = -1;
                                int factorDir = (varPor<0)?-1:1;
                                double Margen = (Constantes.LARGO_ARCO/4)*((Jugador.getX()>0)?factorDir:factorDir*-1);
                                Objetivo = new Posicion(Constantes.centroArcoSup.getX()+Margen,Constantes.centroArcoSup.getY());
                                Fuerza = 1;

                            }else{
                                //Intentar algunso tiros, si fallan...
                                if (
                                        (Dist<30) &&
                                        (estadoVoleas.Efectividad()>=0.5)
                                    ){
                                        estadoVoleas.setChute();
                                        Objetivo = Constantes.centroArcoSup;
                                        Fuerza = 1;
                                }else{
                                    Objetivo = Avance(Jugador,Constantes.centroArcoSup,Jugador.indicesMasCercanos(Rivales));
                                    Fuerza = 0;
                                    Destino = Delanteros[1^0^Bucle];
                                    if (!paseViable(Jugador,Propios[Destino])){
                                        Destino = -1;
                                    }
                                }
                            }
                        }
                    }else{
                        Fuerza = fuerzaEstimada(Dist,idJugador);
                    }
                    if (Fuerza == 0){
                        if (Destino != -1){
                            estadoPase.addPase(Destino, idPase, idJugador);
                            Objetivo = Propios[Destino];
                        }else{
                            //TODO: Matizar, tal vez sea posible un tiro a puerta.
                            Objetivo = Constantes.centroArcoSup;
                        }
                        Dist = Jugador.distancia(Objetivo);
                        Fuerza = fuerzaEstimada(Dist,idJugador);
                    }else{
                        Dist = Jugador.distancia(Objetivo);
                    }
                    Bombeo = (Dist>20)?true:false;
                    comandos.add(new ComandoGolpearBalon(idJugador, Objetivo, Fuerza, Bombeo));
                }
            }
        }

        /**
         * Reajustar las ordenes si hay maniobras o pases en marcha.
         */
        if (estadoManiobra.Ejecutando()){
        //Comprobar las maniobras.
            comandos = estadoManiobra.Ejecutar(comandos, puedenRematar,true);
        }

        if (estadoPase.hayPase()){
            //Reajustar las ordenes segun los pases.
            if (posAntBalon != null){
                Objetivo = this.interceptarBalon(posAntBalon, estadoPase.idDestinatario);
            }else{
                Objetivo = Propios[estadoPase.idDestinatario];
            }
            comandos.add(new ComandoIrA(estadoPase.idDestinatario,Objetivo));

        }

        //Guardar la posición de los rivales.
        for (int Bucle = 0;Bucle<Rivales.length;Bucle++){
            if (posAntRivales[Bucle]!=Rivales[Bucle]){
                posAntRivales[Bucle] = Rivales[Bucle];
            }

        }

        //retorna la lista de comandos.
        return comandos;
    }
}
